package cn.fuck;

import java.util.Arrays;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Bank {
    // \锁对象
    private static Lock bankLock = new ReentrantLock();
    private final double[] accounts;
    private Condition sufficientFunds;
    public Bank(int n, double initialBalance) {
        accounts = new double[n];
        Arrays.fill(accounts, initialBalance);
        sufficientFunds = bankLock.newCondition();
    }

    public synchronized void transfer(int from, int to, double amount) throws InterruptedException
// CAUTION: unsafe when called from multiple threads
    {
        // 使用ReentrantLock保护此方法
        //bankLock.lock();  // 直接使用内部对象锁
        try {
            // 线程进入临界区,
            // 需要满足余额大于出账金额才可以继续操作,
            // 就需要条件对象
            while (accounts[from] < amount) {
                /*
                Condition 方法必须被命名为 await、signalAll 和 signal
                以便它们不会与那些方法发生冲突。
                 */
                //sufficientFunds.await();

                /* 每一个Java对象都有一个内部锁,
                内部对象锁只有一个相关条件
                wait、notifyAll 以及 notify 方法是 Object 类的 final 方法

                调用wait()线程进人等待状态直到它被通知。
                该方法只能在一个同步方法中调用。
                如果当前线程 不是 对象锁的持有者，该方法拋出一个 IllegalMonitorStateException 异常
                 */
                wait();
                System.out.printf("from last %f...wait...to need %f\n", accounts[from], amount);
            }
            System.out.print(Thread.currentThread());
            accounts[from] -= amount; // 非原子操作放
            System.out.printf(" %10.2f from %d to %d, from last %f", amount, from, to, accounts[from]);
            accounts[to] += amount;  // 非原子操作
            System.out.printf(" Total Balance: %10.2f\n", getTotalBalance());
            //
            //sufficientFunds.signalAll();

            /*
            解除那些在该对象上调用 wait 方法的线程的阻塞状态。
            该方法只能在同步方法或同步块内部调用。
            如果当前线程不是对象锁的持有者，
            该方法拋出一个 IllegalMonitorStateException异常
             */
            notifyAll();
        } finally {
            //bankLock.unlock();
        }
    }

    public double getTotalBalance() {
        double sum = 0;
        bankLock.lock();
        try {
            for (double account : accounts) {
                sum += account;
            }
        } finally {
            bankLock.unlock();
        }
        return sum;
    }

    public int size() {
        return accounts.length;
    }


}
